﻿using System;

using System.Collections.Generic;
using System.ComponentModel;

namespace Library.OrmEnterpriseEntity.TableInfo {
	public class OrmBindingList<T> : BindingList<T>, IList<T>, IOrmBindingList, IDisposable, ITypedList where T : DbTable, new() {
		public OrmBindingList() {
			Init();
			DbDeleting = new List<DeletingItem>();
		}
		private PropertyDescriptorCollection _propertyCollection;
		private string _listName;
		private Dictionary<string, PropertyDescriptorCollection> _childCollectionProperties;
		private void Init() {
			var tType = typeof(T);
			_listName = tType.FullName;
			var propertyCollection = TypeDescriptor.GetProperties(tType);
			var listProperties = new List<PropertyDescriptor>();

			foreach (PropertyDescriptor property in propertyCollection) {
				ColumnPropertyDescriptor propertyDescriptor = null;
				var propertyType = property.PropertyType;
				if (propertyType == DateColumn.DateColumnType || propertyType == DateTimeColumn.DateTimeColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, DateColumn.ColumnType, ColumnPropertyDescriptor.DateConverter);
				} else if (propertyType == Int32Column.Int32ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, Int32Column.ColumnType, ColumnPropertyDescriptor.Int32Converter);
				} else if (propertyType == BooleanColumn.BooleanColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, BooleanColumn.ColumnType, ColumnPropertyDescriptor.BooleanConverter);
				} else if (propertyType == Int64Column.Int64ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, Int64Column.ColumnType, ColumnPropertyDescriptor.Int64Converter);
				} else if (propertyType == Int16Column.Int16ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, Int16Column.ColumnType, ColumnPropertyDescriptor.Int16Converter);
				} else if (propertyType == ByteColumn.ByteColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, ByteColumn.ColumnType, ColumnPropertyDescriptor.ByteConverter);
				} else if (propertyType == StringColumn.StringColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, StringColumn.StringColumnType, ColumnPropertyDescriptor.StringConverter);
				} else if (propertyType == AnsiColumn.AnsiColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, AnsiColumn.AnsiColumnType, ColumnPropertyDescriptor.StringConverter);
				} else if (propertyType == FixAnsiColumn.FixAnsiColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, FixAnsiColumn.FixAnsiColumnType, ColumnPropertyDescriptor.StringConverter);
				} else if (propertyType == FixUnicodeColumn.FixUnicodeColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, FixUnicodeColumn.FixUnicodeColumnType, ColumnPropertyDescriptor.StringConverter);
				} else if (propertyType == UnicodeColumn.UnicodeColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, UnicodeColumn.UnicodeColumnType, ColumnPropertyDescriptor.StringConverter);
				} else if (propertyType == DoubleColumn.ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, DoubleColumn.ColumnType, ColumnPropertyDescriptor.DoubleConverter);
				} else if (propertyType == SingleColumn.ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, SingleColumn.ColumnType, ColumnPropertyDescriptor.SingleConverter);
				} else if (propertyType == DecimalColumn.ColumnType) {
					propertyDescriptor = new ColumnPropertyDescriptor(property, tType, DecimalColumn.ColumnType, ColumnPropertyDescriptor.DecimalConverter);
				}
				listProperties.Add(propertyDescriptor ?? property);
			}

			_propertyCollection = new PropertyDescriptorCollection(listProperties.ToArray());

		}
		

		public string GetListName(PropertyDescriptor[] listAccessors) {
			return _listName;
		}
		protected override object AddNewCore() {
			var t = new T();
			if (OnThrowValidationEvent != null)
				t.ThrowValidation += OnThrowValidationEvent;
			Add(t);
			return t;
		}
		void ICollection<T>.Add(T item) {
			Add(item);
		}
		/// <summary>
		/// for child override only, the implementation call base Add method.
		/// </summary>
		/// <param name="t"></param>
		public virtual void Add(T t) {
			base.Add(t);
		}

		public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) {
			if (listAccessors == null || listAccessors.Length == 0) {
				return _propertyCollection;
			}
			if (_childCollectionProperties == null)
				_childCollectionProperties = new Dictionary<string, PropertyDescriptorCollection>();

			Type relevantType = listAccessors[listAccessors.Length - 1].PropertyType;
			string typeName = relevantType.FullName;
			if (typeName == null) return _propertyCollection;
			if (_childCollectionProperties.ContainsKey(typeName)) {
				return _childCollectionProperties[typeName];
			}
			PropertyDescriptorCollection props = TypeDescriptor.GetProperties(relevantType);
			_childCollectionProperties.Add(typeName, props);

			return props;
		}
		public void Dispose() {
			ClearItems();
			GC.SuppressFinalize(this);
		}
		/// <summary>
		/// To array of T
		/// </summary>
		/// <returns></returns>
		public T[] ToArray() {
			var array = new T[Count];
			Items.CopyTo(array, 0);
			return array;
		}
		/// <summary>
		/// Get the list of deleted item that items are marked delete.
		/// </summary>
		public List<DeletingItem> DbDeleting { get; private set; }
		protected override void RemoveItem(int index) {

			var item = this[index];
			var args = new ItemListModifyEventArgs<T>(item, ItemModifyType.Deleting);
			OnItemListModify(args);
			if (item.MarkDelete) {
				if (item.State == EntityState.DbNoChange || item.State == EntityState.DbChange) {
					DbDeleting.Add(new DeletingItem(item, index));
				}
			}
			base.RemoveItem(index);
			args.Type = ItemModifyType.Deleted;
			OnItemListModify(args);
		}

		protected override void InsertItem(int index, T item) {
			var args = new ItemListModifyEventArgs<T>(item, ItemModifyType.Adding);
			OnItemListModify(args);
			base.InsertItem(index, item);
			args.Type = ItemModifyType.Added;
			OnItemListModify(args);
		}
		/// <summary>
		/// 
		/// </summary>
		public class DeletingItem {
			public DeletingItem(T item, int index) {
				Item = item;
				Index = index;
			}
			public T Item { get; private set; }
			public int Index { get; private set; }
		}
		/// <summary>
		/// Insert all item from DbDeleting list, and clear DbDeleting.
		/// </summary>
		public void RestoreFromDeleting() {
			for (int i = DbDeleting.Count - 1; i >= 0; i--) {
				var item = DbDeleting[i];
				if (item.Index < 0 | item.Index > Count)
					Add(item.Item);
				else
					Insert(item.Index, item.Item);
			}
			DbDeleting.Clear();
		}
		/// <summary>
		/// Clear all item from DbDeleting
		/// </summary>
		public void ActualDelete() {
			DbDeleting.Clear();
		}

		public event EventHandler<ItemListModifyEventArgs<T>> ItemListModify;
		private event EventHandler<ThrowValidationExceptionEventArgs> OnThrowValidationEvent;
		public event EventHandler<ThrowValidationExceptionEventArgs> ItemThrowValidation {
			add {
				OnThrowValidationEvent += value;
				foreach (var item in Items) {
					item.ThrowValidation += value;
				}
			}
			remove {
				OnThrowValidationEvent -= value;
				foreach (var item in Items) {
					item.ThrowValidation -= value;
				}
			}
		}

		protected virtual void OnItemListModify(ItemListModifyEventArgs<T> e) {
			var handler = ItemListModify;
			if (handler != null) handler(this, e);
		}
	}
}
